home *** CD-ROM | disk | FTP | other *** search
- /*
- ** File: Switch Stack.c
- **
- ** Contains: A simple example of a VBL written in THINK C that runs on a
- ** private stack.
- **
- ** Written by: Jim Luther (Based on the VBL code from the Technical Note
- ** "MultiFinder Miscellanea".)
- **
- ** Copyright: © 1993 by Apple Computer, Inc., all rights reserved.\
- **
- ** Change History (most recent first):
- **
- ** <3> 11/02/93 JML Added reentrancy comment to VBL
- ** <2> 10/13/93 JML Minor cleanup
- ** <1> 10/08/93 JML First pass
- **
- */
-
- #include "Switch Stack.h"
-
- /*----------------------------------------------------------------------------*/
-
- /*
- ** A global which will be referenced from our VBL Task and the test program
- */
-
- long gCounter; /* Counter incremented each time our VBL gets called */
-
- /*----------------------------------------------------------------------------*/
-
- /*
- ** GetVBLRec returns the address of the VBLRec associated with our VBL task.
- ** This works because on entry into the VBL task, A0 points to the theVBLTask
- ** field in the VBLRec record, which is the first field in the record and that
- ** is the address we return. Note that this method works whether the VBLRec
- ** is allocated globally, in the heap (as long as the record is locked in
- ** memory) or if it is allocated on the stack as is the case in this example.
- ** In the latter case this is OK as long as the procedure which installed the
- ** task does not exit while the task is running. This trick allows us to get
- ** to the saved A5, but it could also be used to get to anything we wanted to
- ** store in the record.
- */
- VBLRecPtr GetVBLRec ()
- = 0x2008; /* MOVE.L A0,D0 */
-
- /*----------------------------------------------------------------------------*/
-
- /*
- ** DoVBL is called only by StartVBL ()
- */
- void DoVBL (VBLRecPtr recPtr)
- {
- gCounter++; /* Show we can set a global */
- recPtr->theVBLTask.vblCount = INTERVAL; /* Set ourselves to run again */
- }
-
- /*----------------------------------------------------------------------------*/
-
- /*
- ** This is the actual VBL task code. It uses GetVBLRec to get our VBL record
- ** and properly set up A5. Having done that, switches to a private stack and
- ** then calls DoVBL to increment a global counter and sets itself to run again.
- ** Because of the vagaries of C optimization, it calls a separate routine to
- ** actually access global variables. See M.OV.A5 - "Setting and Restoring A5"
- ** for the reasons for this, as well as for a description of SetA5.
- ** I can switch to my private stack without checking to make sure I haven't
- ** already done so because VBLs don't have to worry about begin reentrant.
- */
- void StartVBL ()
- {
- long curA5;
- VBLRecPtr recPtr;
-
- recPtr = GetVBLRec (); /* First get our record */
- curA5 = SetA5 (recPtr->VBLA5); /* Get the saved A5 */
- /* Now we can access globals */
-
- /* Switch to private stack */
- asm
- {
- MOVEA.L recPtr,A0 ; A0 points to VBLRec
- MOVE.L HiHeapMark,VBLRec.savedHiHeapMark(A0) ; save HiHeapMark
- MOVE.L StkLowPt,VBLRec.savedStkLowPt(A0) ; save StkLowPt
- MOVE.L A7,VBLRec.savedA7(A0) ; save current system stack
- CLR.L StkLowPt ; disable the stack sniffer
- MOVE.L VBLRec.ourStackBottom(A0),HiHeapMark ; set HiHeapMark to bottom of our stack
- MOVEA.L VBLRec.ourStackTop(A0),A7 ; switch stacks
- MOVE.L A0,-(SP) ; save A0 on top of private stack
- }
-
- /* Now we're running on our private stack */
-
- DoVBL(recPtr); /* Call another routine to do actual work */
-
- /* Switch back to original stack */
- asm
- {
- MOVE.L (SP)+,A0 ; restore A0 from private stack */
- MOVEA.L VBLRec.savedA7(A0),A7 ; restore system stack
- MOVE.L VBLRec.savedHiHeapMark(A0),HiHeapMark ; restore HiHeapMark
- MOVE.L VBLRec.savedStkLowPt(A0),StkLowPt ; restore the sniffer
- }
-
- (void) SetA5 (curA5); /* Restore old A5 */
- }
-
- /*----------------------------------------------------------------------------*/
-
- /*
- ** Create a dialog just to demonstrate that the global variable
- ** is being updated by the VBL Task. Before installing the VBL, we store
- ** our A5 in the actual VBL Task record, using SetCurrentA5 described in
- ** TM.OV.A5. We also store the location of our private stack which the VBL code
- ** will run under. We'll run the VBL, showing the counter being incremented,
- ** until the mouse button is clicked. Then we remove the VBL Task, close the
- ** dialog, and remove the mouse down events to prevent the application from
- ** being inadvertently switched by MultiFinder.
- */
- void main (void)
- {
- VBLRec theVBLRec;
- DialogPtr infoDPtr;
- DialogRecord infoDStorage;
- Str255 numStr = "\pTest";
- OSErr theErr;
- Handle theItemHandle;
- short theItemType;
- Rect theRect;
- long lastCount = 0;
-
- MaxApplZone();
- InitGraf((Ptr)&qd.thePort);
- InitFonts();
- InitWindows();
- InitMenus();
- TEInit();
- InitDialogs(nil);
- InitCursor();
-
- /* Store the current value of A5 in the VBLA5 field. */
- theVBLRec.VBLA5 = SetCurrentA5 ();
-
- /* Allocate memory for our private stack and store it's location in ourStackBottom */
- theVBLRec.ourStackBottom = NewPtrSys(kStackSize);
- if (theVBLRec.ourStackBottom != nil)
- {
- /* Store location of our stack's top in ourStackTop */
- theVBLRec.ourStackTop =
- (Ptr)((unsigned long)theVBLRec.ourStackBottom + kStackSize);
-
- gCounter = 0; /* Initialize our global counter */
-
- /* Put up the dialog */
- infoDPtr = GetNewDialog (rInfoDialog, (Ptr) &infoDStorage, (WindowPtr) -1);
- DrawDialog (infoDPtr);
- GetDItem (infoDPtr, rStatTextItem, &theItemType, &theItemHandle,
- &theRect);
-
- /* Set the address of our routine */
- theVBLRec.theVBLTask.vblAddr = (VBLProcPtr)StartVBL;
- theVBLRec.theVBLTask.vblCount = INTERVAL; /* Frequency of task, in ticks */
- theVBLRec.theVBLTask.qType = vType; /* qElement is a VBL task */
- theVBLRec.theVBLTask.vblPhase = 0;
-
- /* Now install the VBL task */
- theErr = VInstall((QElemPtr)&theVBLRec.theVBLTask);
-
- /* Display the counter until the mouse button is pushed */
- if (!theErr)
- {
- do
- {
- if (gCounter != lastCount)
- {
- lastCount = gCounter;
- NumToString(gCounter, numStr);
- SetIText(theItemHandle, numStr);
- }
- } while (!Button ());
- theErr = VRemove((QElemPtr)&theVBLRec.theVBLTask); /* Remove it when done */
- }
-
- /* Finish up */
- CloseDialog (infoDPtr); /* Get rid of our dialog */
- FlushEvents (mDownMask, 0); /* Flush all mouse down events */
-
- /* Dispose of the private stack */
- DisposePtr(theVBLRec.ourStackBottom);
- }
- }
-